/*
 * Texture Manager
 */

#include "zgl.h"

static GLTexture* find_texture(GLContext* c, int h) {
  GLTexture* t;

  t = c->shared_state.texture_hash_table[h % TEXTURE_HASH_TABLE_SIZE];
  while (t != NULL) {
    if (t->handle == h) return t;
    t = t->next;
  }
  return NULL;
}

static void free_texture(GLContext* c, int h) {
  GLTexture *t, **ht;
  GLImage* im;
  int i;

  t = find_texture(c, h);
  if (t->prev == NULL) {
    ht = &c->shared_state.texture_hash_table[t->handle % TEXTURE_HASH_TABLE_SIZE];
    *ht = t->next;
  } else {
    t->prev->next = t->next;
  }
  if (t->next != NULL) t->next->prev = t->prev;

  for (i = 0; i < MAX_TEXTURE_LEVELS; i++) {
    im = &t->images[i];
    if (im->pixmap != NULL) gl_free(im->pixmap);
  }

  gl_free(t);
}

GLTexture* alloc_texture(GLContext* c, int h) {
  GLTexture *t, **ht;

  t = gl_zalloc(sizeof(GLTexture));

  ht = &c->shared_state.texture_hash_table[h % TEXTURE_HASH_TABLE_SIZE];

  t->next = *ht;
  t->prev = NULL;
  if (t->next != NULL) t->next->prev = t;
  *ht = t;

  t->handle = h;

  return t;
}

void glInitTextures(GLContext* c) {
  /* textures */

  c->texture_2d_enabled = 0;
  c->current_texture = find_texture(c, 0);
}

void glGenTextures(int n, unsigned int* textures) {
  GLContext* c = gl_get_context();
  int max, i;
  GLTexture* t;

  max = 0;
  for (i = 0; i < TEXTURE_HASH_TABLE_SIZE; i++) {
    t = c->shared_state.texture_hash_table[i];
    while (t != NULL) {
      if (t->handle > max) max = t->handle;
      t = t->next;
    }
  }
  for (i = 0; i < n; i++) {
    textures[i] = max + i + 1;
  }
}

void glDeleteTextures(int n, const unsigned int* textures) {
  GLContext* c = gl_get_context();
  int i;
  GLTexture* t;

  for (i = 0; i < n; i++) {
    t = find_texture(c, textures[i]);
    if (t != NULL && t != 0) {
      if (t == c->current_texture) {
        glBindTexture(GL_TEXTURE_2D, 0);
      }
      free_texture(c, textures[i]);
    }
  }
}

void glopBindTexture(GLContext* c, GLParam* p) {
  int target = p[1].i;
  int texture = p[2].i;
  GLTexture* t;

  assert(target == GL_TEXTURE_2D && texture >= 0);

  t = find_texture(c, texture);
  if (t == NULL) {
    t = alloc_texture(c, texture);
  }
  c->current_texture = t;
}

void glopTexImage2D(GLContext* c, GLParam* p) {
  int target = p[1].i;
  int level = p[2].i;
  int components = p[3].i;
  int width = p[4].i;
  int height = p[5].i;
  int border = p[6].i;
  int format = p[7].i;
  int type = p[8].i;
  void* pixels = p[9].p;
  GLImage* im;
  unsigned char* pixels1;
  int do_free;

  if (!(target == GL_TEXTURE_2D && level == 0 && components == 3 && border == 0 &&
        format == GL_RGB && type == GL_UNSIGNED_BYTE)) {
    gl_fatal_error("glTexImage2D: combinaison of parameters not handled");
  }

  do_free = 0;
  if (width != 256 || height != 256) {
    pixels1 = gl_malloc(256 * 256 * 3);
    /* no interpolation is done here to respect the original image aliasing ! */
    gl_resizeImageNoInterpolate(pixels1, 256, 256, pixels, width, height);
    do_free = 1;
    width = 256;
    height = 256;
  } else {
    pixels1 = pixels;
  }

  im = &c->current_texture->images[level];
  im->xsize = width;
  im->ysize = height;
  if (im->pixmap != NULL) gl_free(im->pixmap);
#if TGL_FEATURE_RENDER_BITS == 24
  im->pixmap = gl_malloc(width * height * 3);
  if (im->pixmap) {
    memcpy(im->pixmap, pixels1, width * height * 3);
  }
#elif TGL_FEATURE_RENDER_BITS == 32
  im->pixmap = gl_malloc(width * height * 4);
  if (im->pixmap) {
    gl_convertRGB_to_8A8R8G8B(im->pixmap, pixels1, width, height);
  }
#elif TGL_FEATURE_RENDER_BITS == 16
  im->pixmap = gl_malloc(width * height * 2);
  if (im->pixmap) {
    gl_convertRGB_to_5R6G5B(im->pixmap, pixels1, width, height);
  }
#else
#error TODO
#endif
  if (do_free) gl_free(pixels1);
}

/* TODO: not all tests are done */
void glopTexEnv(GLContext* c, GLParam* p) {
  int target = p[1].i;
  int pname = p[2].i;
  int param = p[3].i;

  if (target != GL_TEXTURE_ENV) {
  error:
    gl_fatal_error("glTexParameter: unsupported option");
  }

  if (pname != GL_TEXTURE_ENV_MODE) goto error;

  if (param != GL_DECAL) goto error;
}

/* TODO: not all tests are done */
void glopTexParameter(GLContext* c, GLParam* p) {
  int target = p[1].i;
  int pname = p[2].i;
  int param = p[3].i;

  if (target != GL_TEXTURE_2D) {
  error:
    gl_fatal_error("glTexParameter: unsupported option");
  }

  switch (pname) {
    case GL_TEXTURE_WRAP_S:
    case GL_TEXTURE_WRAP_T:
      if (param != GL_REPEAT) goto error;
      break;
  }
}

void glopPixelStore(GLContext* c, GLParam* p) {
  int pname = p[1].i;
  int param = p[2].i;

  if (pname != GL_UNPACK_ALIGNMENT || param != 1) {
    gl_fatal_error("glPixelStore: unsupported option");
  }
}
